﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using GeoFramework.Gps;
using GeoFramework.Gps.IO;


namespace NavsGo
{
    public partial class MonitorForm : Form
    {
        GeoFramework.Gps.Nmea.NmeaInterpreter Interpreter = new GeoFramework.Gps.Nmea.NmeaInterpreter();
        Timer localTimeTimer = new Timer();
        public MonitorForm()
        {
            InitializeComponent();

            /* GPS.NET provides the ability to quickly discover GPS devices on the
            * local system.  This feature is known as "Automatic Device Discovery"
            * and greatly simplifies the task of finding a GPS device to communicate
            * with.  Developers can simply call the "Start" method of an interpreter,
            * and GPS.NET will automatically locate the best GPS Device to work with.
            * 
            * During Device Discovery, several events are raised to indicate the
            * progress of detection.  This example hooks into almost all of the events
            * to give users feedback.
            */
            GeoFramework.Gps.IO.Devices.DeviceDetectionStarted += new EventHandler(Devices_DeviceDetectionStarted);
            GeoFramework.Gps.IO.Devices.DeviceDetected += new EventHandler<GeoFramework.Gps.IO.DeviceEventArgs>(Devices_DeviceDetected);
            GeoFramework.Gps.IO.Devices.DeviceDetectionCompleted += new EventHandler(Devices_DeviceDetectionCompleted);
            GeoFramework.Gps.IO.Devices.DeviceDetectionAttempted += new EventHandler<GeoFramework.Gps.IO.DeviceEventArgs>(Devices_DeviceDetectionAttempted);
            GeoFramework.Gps.IO.Devices.DeviceDetectionAttemptFailed += new EventHandler<GeoFramework.Gps.IO.DeviceDetectionExceptionEventArgs>(Devices_DeviceDetectionAttemptFailed);

            // 
            // Interpreter
            // 
            Interpreter.SpeedChanged += new System.EventHandler<GeoFramework.SpeedEventArgs>(Interpreter_SpeedChanged);
            Interpreter.FixAcquired += new System.EventHandler(Interpreter_FixAcquired);
            Interpreter.FixLost += new System.EventHandler(Interpreter_FixLost); 
            Interpreter.Resumed += new EventHandler(Interpreter_Resumed); 
            Interpreter.FixModeChanged += new System.EventHandler<GeoFramework.Gps.FixModeEventArgs>(Interpreter_FixModeChanged); 
            Interpreter.FixQualityChanged += new System.EventHandler<GeoFramework.Gps.FixQualityEventArgs>(Interpreter_FixQualityChanged);
            Interpreter.HorizontalDilutionOfPrecisionChanged += new System.EventHandler<GeoFramework.Gps.DilutionOfPrecisionEventArgs>(Interpreter_HorizontalDilutionOfPrecisionChanged); 
            Interpreter.SentenceReceived += new System.EventHandler<GeoFramework.Gps.Nmea.NmeaSentenceEventArgs>(Interpreter_SentenceReceived); 
            Interpreter.AltitudeChanged += new System.EventHandler<GeoFramework.DistanceEventArgs>(Interpreter_AltitudeChanged); 
            Interpreter.PositionChanged += new System.EventHandler<GeoFramework.PositionEventArgs>(Interpreter_PositionChanged); 
            Interpreter.UtcDateTimeChanged += new System.EventHandler<GeoFramework.DateTimeEventArgs>(Interpreter_UtcDateTimeChanged); 
            Interpreter.Paused += new EventHandler(Interpreter_Paused); 
            Interpreter.BearingChanged += new System.EventHandler<GeoFramework.AzimuthEventArgs>(Interpreter_BearingChanged); 
            Interpreter.VerticalDilutionOfPrecisionChanged += new System.EventHandler<GeoFramework.Gps.DilutionOfPrecisionEventArgs>(Interpreter_VerticalDilutionOfPrecisionChanged);
            Interpreter.SatellitesChanged += new EventHandler<SatelliteListEventArgs>(Interpreter_SatellitesChanged);
            //Timer
            localTimeTimer.Enabled = true;
            localTimeTimer.Interval = 500;
            localTimeTimer.Tick += new System.EventHandler(this.LocalTimeTimer_Tick);

            compass1.IsPaintingOnSeparateThread = false;
            altimeter1.IsPaintingOnSeparateThread = false;
            speedometer1.IsPaintingOnSeparateThread = false;
            //satelliteViewer1.IsPaintingOnSeparateThread = false;
            //satelliteSignalBar1.IsPaintingOnSeparateThread = false;
        }

        
        #region GPS Device Detection Events
        private void SearchButton_Click(object sender, EventArgs e)
        {
            // Start the GPS device detection process.  This operation will complete
            // in the background, on a separate thread.  This method can be called again
            // to attempt to locate new devices.
            GeoFramework.Gps.IO.Devices.BeginDetection();
        }

        private void Devices_DeviceDetectionStarted(object sender, EventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                StatusBar1.Text = "Detecting GPS devices...";
                SearchButton.Enabled = false;
            }));
        }

        private void Devices_DeviceFailure(object sender, GeoFramework.Gps.IO.DeviceEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                StatusBar1.Text = "Not Responding: " + e.Device.ToString();
            }));
        }

        private void Devices_DeviceDetectionAttemptFailed(object sender, GeoFramework.Gps.IO.DeviceDetectionExceptionEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                StatusBar1.Text = "Error: " + e.Exception.ToString();
            }));
        }

        private void Devices_DeviceDetectionCompleted(object sender, EventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                StatusBar1.Text = "Device detection completed.";
                SearchButton.Enabled = true;
                StartButton.Enabled = true;
            }));
        }

        private void Devices_DeviceDetectionAttempted(object sender, GeoFramework.Gps.IO.DeviceEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                StatusBar1.Text = "Detecting " + e.Device.ToString();
            }));
        }

        private void Devices_DeviceDetected(object sender, GeoFramework.Gps.IO.DeviceEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                StatusBar1.Text = "Found Device: " + e.Device.ToString();

                // Add it to the list box
                ListViewItem DeviceItem = new ListViewItem(e.Device.ToString());
                // Associate the detected device with this list view item, so we can connect 
                // to the device later on
                DeviceItem.Tag = e.Device;
                // Set the image of the new item
                DeviceItem.ImageIndex = 0;
                DevicesListView.Items.Add(DeviceItem);
            }));
        }
        #endregion

        #region Connecting and Disconnecting
        private void StartButton_Click(object sender, EventArgs e)
        {
            if (DevicesListView.SelectedIndices.Count == 0)
            {
                MessageBox.Show("Please Select the device to start.");
                return;
            }
            // Find out which item is selected in the list view
            ListViewItem SelectedItem = DevicesListView.Items[DevicesListView.SelectedIndices[0]];

            // The "tag" property holds the device that was detected
            Device selectedDevice = (Device)SelectedItem.Tag;
            
            try
            {
                // Finally, start the interpreter using the newly-assigned Stream
                Interpreter.Start(selectedDevice);

                selectedDevice.Connecting += new EventHandler(Device_Connecting);
                selectedDevice.Connected += new EventHandler(Device_Connected);
                selectedDevice.Disconnecting += new EventHandler(Device_Disconnecting);
                selectedDevice.Disconnected += new EventHandler(Device_Disconnected);

                StartButton.Enabled = false;
                // Enable the disconnect button
                StopButton.Enabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Unable to Connect", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
            }
        }

        private void StopButton_Click(object sender, EventArgs e)
        {
            // Stop any GPS communications
            Interpreter.Stop();

            StartButton.Enabled = true;
            // And disable the disconnect button
            StopButton.Enabled = false;
            StatusBar1.Text = "Disconnected from GPS device.";
        }

        private void DevicesListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Enable the Connect button
            StartButton.Enabled = true;
            StartButton.Focus();
        }

        void Device_Connecting(object sender, EventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;
            StatusBar1.Text = "Connecting to GPS device...";
        }
        
        void Device_Connected(object sender, EventArgs e)
        {
            StatusBar1.Text = "Connected to GPS device!  Reading data...";
            Cursor.Current = Cursors.Default;
        }

        void Device_Disconnecting(object sender, System.EventArgs e)
        {
            try
            {
                StatusBar1.Text = "Disconnecting from GPS device...";
                Cursor.Current = Cursors.WaitCursor;
            }
            catch
            {
            }
        }

        void Device_Disconnected(object sender, System.EventArgs e)
        {
            try
            {
                StatusBar1.Text = "Disconnected.";
                Cursor.Current = Cursors.Default;
            }
            catch
            {
            }
        }


        //void Interpreter_TimeoutOccurred(object sender, ExceptionEventArgs e)
        //{
        //    StatusBar1.Text = "The device stopped.  Restarting...";
        //}
        #endregion

        #region Working with Raw Data
        void Interpreter_SentenceReceived(object sender, GeoFramework.Gps.Nmea.NmeaSentenceEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Keep the list box from getting too big
                if (RawDataListBox.Items.Count > 50)
                    RawDataListBox.Items.RemoveAt(0);

                // Add the sentence to the raw data list box
                RawDataListBox.Items.Add(e.Sentence.ToString());
                RawDataListBox.SelectedIndex = RawDataListBox.Items.Count - 1;
            }));
        }
        #endregion

        #region GPS Information Changed
        void Interpreter_BearingChanged(object sender, GeoFramework.AzimuthEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                compass1.Value = e.Azimuth;

                // Output the current bearing as degrees (i.e. 012°)
                Bearing.Text = e.Azimuth.ToString();
                // Output the current bearing as a compass direction (i.e. Southwest)
                Direction.Text = e.Azimuth.ToString("c");
                StatusBar1.Text = "Bearing has changed.";
            }));
        }

        void Interpreter_UtcDateTimeChanged(object sender, GeoFramework.DateTimeEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Update the current satellite-derived time
                SatelliteTime.Text = e.DateTime.ToLocalTime().ToString();
            }));
        }

        void Interpreter_PositionChanged(object sender, GeoFramework.PositionEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Update the latitude and longitude
                Latitude.Text = e.Position.Latitude.ToString();
                Longitude.Text = e.Position.Longitude.ToString();
                StatusBar1.Text = "Position has changed.";
            }));
        }

        void Interpreter_AltitudeChanged(object sender, GeoFramework.DistanceEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                altimeter1.Value = e.Distance;
                // Update the current altitude
                Altitude.Text = e.Distance.ToLocalUnitType().ToString();
                StatusBar1.Text = "Altitude has changed.";
            }));
        }

        void Interpreter_HorizontalDilutionOfPrecisionChanged(object sender, GeoFramework.Gps.DilutionOfPrecisionEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Update the horizontal DOP on the track bar
                CurrentHdopTrackBar.Value = (int)e.DilutionOfPrecision.Value;
                UpdateDopStatus();
                StatusBar1.Text = "Lat/long precision has changed.";
            }));
        }

        void Interpreter_VerticalDilutionOfPrecisionChanged(object sender, GeoFramework.Gps.DilutionOfPrecisionEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Update the vertical DOP on the track bar
                CurrentVdopTrackBar.Value = (int)e.DilutionOfPrecision.Value;
                UpdateDopStatus();
                StatusBar1.Text = "Altitude precision has changed.";
            }));
        }

        private void UpdateDopStatus()
        {
            // Are readings now precise enough to use?
            if (CurrentHdopTrackBar.Value <= MaximumHdopTrackBar.Value)
            {
                HdopStatusLabel.Text = "Position readings are being accepted.";
            }
            else
            {
                HdopStatusLabel.Text = "Position readings are being ignored.";
            }
            
            // Are readings now precise enough to use?
            if (CurrentVdopTrackBar.Value <= MaximumVdopTrackBar.Value)
            {
                VdopStatusLabel.Text = "Altitude readings are being accepted.";
            }
            else
            {
                VdopStatusLabel.Text = "Altitude readings are being ignored.";
            }
        }
        void Interpreter_FixQualityChanged(object sender, GeoFramework.Gps.FixQualityEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Update the current fix quality
                FixQuality.Text = e.FixQuality.ToString();
                StatusBar1.Text = "Fix quality has changed.";
            }));
        }

        void Interpreter_FixModeChanged(object sender, GeoFramework.Gps.FixModeEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Update the current fix mode
                FixMode.Text = e.FixMode.ToString();
                StatusBar1.Text = "Fix mode has changed.";
            }));
        }

        void Interpreter_FixLost(object sender, System.EventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Change the fix status
                FixStatus.Text = "Fix Lost";
                StatusBar1.Text = "The current fix was lost.";
            }));
        }

        void Interpreter_FixAcquired(object sender, System.EventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                // Change the fix status            
                FixStatus.Text = "Fix Obtained!";
                StatusBar1.Text = "A fix has been obtained!";
            }));
        }

        void Interpreter_SpeedChanged(object sender, GeoFramework.SpeedEventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                speedometer1.Value = e.Speed;

                // Update the current speed
                Speed.Text = e.Speed.ToLocalUnitType().ToString();
                StatusBar1.Text = "Speed has changed.";
            }));
        }

        void Interpreter_SatellitesChanged(object sender, SatelliteListEventArgs e)
        {
            //satelliteViewer1.Satellites = e.Satellites as List<Satellite>;
            //satelliteSignalBar1.Satellites = e.Satellites as List<Satellite>;
        }
        #endregion

        #region Pause and Resume
        void Interpreter_Paused(object sender, EventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {   
                StatusBar1.Text = "Device was powered off!";
                Cursor.Current = Cursors.WaitCursor;
            }));
        }

        void Interpreter_Resumed(object sender, EventArgs e)
        {
            BeginInvoke(new MethodInvoker(delegate()
            {
                Cursor.Current = Cursors.Default;
                StatusBar1.Text = "Device is no longer suspended!  Restarting...";
            }));
        }
        #endregion


        #region Dilution Of Precision
        private void MaximumVdopTrackBar_ValueChanged(object sender, EventArgs e)
        {
            if (MaximumVdopTrackBar.Value > MaximumVdopTrackBar.Maximum)
            {
                MaximumVdopTrackBar.Value = MaximumVdopTrackBar.Maximum;
            }
            else if (MaximumVdopTrackBar.Value <= MaximumVdopTrackBar.Minimum)
            {
                MaximumVdopTrackBar.Value = MaximumVdopTrackBar.Minimum + 1;
                if (MaximumVdopTrackBar.Value == 0)
                {
                    MaximumVdopTrackBar.Value = 1;
                }
            }

            // Set the new maximum altitude precision error
            Interpreter.MaximumVerticalDilutionOfPrecision =
                new DilutionOfPrecision(MaximumVdopTrackBar.Value);
            // And update the UI
            UpdateDopStatus();
        }

        private void MaximumHdopTrackBar_ValueChanged(object sender, EventArgs e)
        {
            if (MaximumHdopTrackBar.Value > MaximumHdopTrackBar.Maximum)
            {
                MaximumHdopTrackBar.Value = MaximumHdopTrackBar.Maximum;
            }
            else if (MaximumHdopTrackBar.Value <= MaximumHdopTrackBar.Minimum)
            {
                MaximumHdopTrackBar.Value = MaximumHdopTrackBar.Minimum;
                if (MaximumHdopTrackBar.Value == 0)
                {
                    MaximumHdopTrackBar.Value = 1;
                }
            }
            // Set the new maximum latitude/longitude precision error
            Interpreter.MaximumHorizontalDilutionOfPrecision =
                new DilutionOfPrecision(MaximumHdopTrackBar.Value);
            // And update the UI
            UpdateDopStatus();
        }
        #endregion

        private void LocalTimeTimer_Tick(object sender, EventArgs e)
        {
            LocalTime.Text = DateTime.Now.ToString();
        }
    }
}